const {
    OPTION_PAGE_SIZE,
    shareData,
    eval_code,
} = require("../commands/share_data");
const {
    toggleRawMode,
    showOptions,
    clearLine,
    resetOptions,
} = require("./interaction_tool");
const {
    info,
    whisper,
    log,
    success,
    getPrint,
    warn,
    debug,
} = require("./log_tool");
const { toArray } = require("./transform_tool");

async function select(content, data) {
    // let data = this.selfData;
    // let content = this.content;
    info("");
    // 支持数组以及普通对象
    let optionKeys = Object.keys(content);
    const isContentArray = Array.isArray(content);
    // 过滤
    const keyFilterRegExp =
        data.key_filter_pattern && new RegExp(data.key_filter_pattern);
    const valueFilterRegExp =
        data.value_filter_pattern && new RegExp(data.value_filter_pattern);
    optionKeys = optionKeys.filter((key) => {
        const value = content[key];
        return (
            true &&
            (!keyFilterRegExp || keyFilterRegExp.test(key)) &&
            (!valueFilterRegExp ||
                valueFilterRegExp.test(JSON.stringify(value)))
        );
    });
    const optionValues = optionKeys.map((key) => content[key]);
    /**
     * @type {any[]}
     */
    const options = content;
    const viewOptions = optionKeys.map((optionKey, index) => {
        const optionValue = options[optionKey];
        const showKey = isContentArray ? +optionKey + 1 : optionKey;
        if (data.preview_handler) {
            return {
                showKey,
                key: optionKey,
                value: eval_code(data.preview_handler)(
                    optionValue,
                    index,
                    optionValues,
                ),
            };
        }
        return {
            showKey,
            key: optionKey,
            value: data.preview
                ? toArray(data.preview)
                      .map((k, i) =>
                          i === -1 ? `${k}:${optionValue[k]}` : optionValue[k],
                      )
                      .join(" | ")
                : typeof optionValue === "object"
                  ? JSON.stringify(optionValue)
                  : optionValue,
        };
    });
    let filteredViewOptions = viewOptions;
    const _searchMaxChars = 20;
    const inputs = [];
    let focusedIndex = 0;
    const automatic = data.automatic;
    // 推荐值
    if (data.recommend_eval) {
        try {
            focusedIndex = Math.max(
                0,
                Math.min(
                    optionKeys.length - 1,
                    parseInt(eval_code(data.recommend_eval), 10),
                ),
            );
        } catch (err) {
            warn(data.recommend_eval);
            warn(`推荐失败: ${err}`);
            focusedIndex = 0;
        }
    }
    if (data.recommend) {
        switch (data.recommend) {
            case "first": {
                focusedIndex = 0;
                break;
            }
            case "last": {
                focusedIndex = optionKeys.length - 1;
                break;
            }
            case "middle": {
                focusedIndex = Math.floor((optionKeys.length - 1) / 2);
                break;
            }
            case "random": {
                focusedIndex = Math.round(
                    (optionKeys.length - 1) * Math.random(),
                );
                break;
            }
            default: {
                warn(`不支持的 recommend: ${data.recommend}`);
                break;
            }
        }
    }
    if (data.recommend_context) {
        try {
            const text = data.recommend_context;
            const match_ret = data.recommend_pattern
                ? new RegExp(data.recommend_pattern).exec(text)
                : [text];
            if (match_ret) {
                for (let i = 0; i < viewOptions.length; i++) {
                    const viewOption = JSON.stringify(viewOptions[i]);
                    if (
                        (data.recommend_only_key &&
                            viewOptions[i].key ===
                                match_ret[match_ret.length - 1]) ||
                        (!data.recommend_only_key &&
                            viewOption.indexOf(
                                match_ret[match_ret.length - 1],
                            ) !== -1)
                    ) {
                        focusedIndex = i;
                        success(
                            `从推荐上下文中获取 ${match_ret[match_ret.length - 1]}`,
                        );
                        break;
                    }
                }
            }
        } catch (err) {
            warn(err);
        }
    }
    const numPerPage = data.OPTION_PAGE_SIZE || OPTION_PAGE_SIZE;
    function restrictFocusedIndex() {
        if (focusedIndex < 0) {
            focusedIndex = 0;
        }
        if (focusedIndex >= viewOptions.length) {
            focusedIndex = viewOptions.length - 1;
        }
    }
    function refreshOptions() {
        showOptions(
            viewOptions,
            filteredViewOptions,
            focusedIndex,
            focusedIndex,
            numPerPage,
            undefined,
            inputs.join(""),
        );
        whisper(`单选: [↑↓方向键]切换 [ENTER回车键]确认`, undefined, true);
        if (inputs.length) {
            clearLine();
            debug(inputs.join(""), "", true);
        } else {
            whisper("可输入使用搜索", "", true);
        }
    }
    const selectedOption = await new Promise((resolve, _reject) => {
        // listen for the "keypress" event
        /**
         *
         * @param {string} ch
         * @param {{ctrl: boolean, alt: boolean, shift: boolean, name: string }} key
         */
        const onKeyPress = (ch, key) => {
            let isKey = false;
            let inputsChg = false;
            if (key) {
                if (key.name === "return" || key.name === "tab") {
                    isKey = true;
                    if (focusedIndex !== -1) {
                        process.stdin.off("keypress", onKeyPress);
                        process.stdin.pause();
                        toggleRawMode(false);
                        log("");
                        let ret;
                        switch (data.select_mode) {
                            case "key": {
                                ret = optionKeys[focusedIndex];
                                break;
                            }
                            case "index": {
                                ret = focusedIndex;
                                break;
                            }
                            case "all": {
                                ret = {
                                    index: focusedIndex,
                                    key: optionKeys[focusedIndex],
                                    value: optionValues[focusedIndex],
                                };
                                break;
                            }
                            default: {
                                ret = optionValues[focusedIndex];
                                break;
                            }
                        }
                        resetOptions();
                        resolve(ret);
                    } else {
                        clearLine();
                        warn("\r" + `请选择有效项`, "", true);
                    }
                    return;
                } else if (key.name === "backspace" || key.name === "delete") {
                    isKey = true;
                    inputsChg = true;
                    inputs.pop();
                } else if (key.name === "left") {
                    isKey = true;
                    focusedIndex -= 1;
                    restrictFocusedIndex();
                } else if (key.name === "right") {
                    isKey = true;
                    focusedIndex += 1;
                    restrictFocusedIndex();
                } else if (key.name === "up") {
                    isKey = true;
                    const filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[Math.max(0, filterIdx - 1)],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "down") {
                    isKey = true;
                    const filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[
                            Math.min(
                                filterIdx + 1,
                                filteredViewOptions.length - 1,
                            )
                        ],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "pageup") {
                    isKey = true;
                    let filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    filterIdx = Math.max(0, filterIdx - numPerPage);
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[filterIdx],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "pagedown") {
                    isKey = true;
                    let filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    filterIdx = Math.min(
                        filteredViewOptions.length - 1,
                        filterIdx + numPerPage,
                    );
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[filterIdx],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "home") {
                    isKey = true;
                    focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
                } else if (key.name === "end") {
                    isKey = true;
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[filteredViewOptions.length - 1],
                    );
                } else if (key.ctrl && key.name === "c") {
                    throw "用户中止";
                    return;
                } else if (key.name === "escape") {
                    isKey = true;
                    inputsChg = true;
                    inputs.length = 0;
                } else if (key.ctrl) {
                    isKey = true;
                }
            }
            if (!isKey && ch) {
                inputs.push(ch);
                inputsChg = true;
            }
            if (inputsChg) {
                focusedIndex = -1;
                const inputStr = inputs.join("");
                const inputParts = inputStr?.split(/[, ]+/).filter((v) => v);
                filteredViewOptions = !inputParts
                    ? viewOptions
                    : viewOptions.filter((option, i) => {
                          const valueStr = JSON.stringify(option.value);
                          return inputParts.some((part) => {
                              return (
                                  part === i ||
                                  part === option.key ||
                                  valueStr.indexOf(part) !== -1
                              );
                          });
                      });
                if (filteredViewOptions.length) {
                    focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
                }
            }
            refreshOptions();
        };
        process.stdin.on("keypress", onKeyPress);
        toggleRawMode(true);
        process.stdin.resume();
        refreshOptions();
        // 全自动返回推荐选项
        if (shareData.FULL_AUTOMATIC || automatic) {
            onKeyPress(undefined, { name: "return" });
        }
    });
    data.verbose && success(`已选择:${getPrint(selectedOption, true)}`);
    return selectedOption;
}

async function multiSelect(content, data) {
    info("");
    // 支持数组以及普通对象
    let optionKeys = Object.keys(content);
    const isContentArray = Array.isArray(content);
    // 过滤
    const keyFilterRegExp =
        data.key_filter_pattern && new RegExp(data.key_filter_pattern);
    const valueFilterRegExp =
        data.value_filter_pattern && new RegExp(data.value_filter_pattern);
    optionKeys = optionKeys.filter((key) => {
        const value = content[key];
        return (
            true &&
            (!keyFilterRegExp || keyFilterRegExp.test(key)) &&
            (!valueFilterRegExp ||
                valueFilterRegExp.test(JSON.stringify(value)))
        );
    });
    const optionValues = optionKeys.map((key) => content[key]);
    /**
     * @type {any[]}
     */
    const options = content;
    const viewOptions = optionKeys.map((optionKey, index) => {
        const optionValue = options[optionKey];
        const showKey = isContentArray ? +optionKey + 1 : optionKey;
        if (data.preview_handler) {
            return {
                showKey,
                key: optionKey,
                value: eval_code(data.preview_handler)(
                    optionValue,
                    index,
                    optionValues,
                ),
            };
        }
        return {
            showKey,
            key: optionKey,
            value: data.preview
                ? toArray(data.preview)
                      .map((k, i) =>
                          i === -1 ? `${k}:${optionValue[k]}` : optionValue[k],
                      )
                      .join(" | ")
                : typeof optionValue === "object"
                  ? JSON.stringify(optionValue)
                  : optionValue,
        };
    });
    let filteredViewOptions = viewOptions;
    const _searchMaxChars = 20;
    const inputs = [];
    let focusedIndex = 0;
    let confirmEmpty = false;
    const selectedIndices = [];
    const numPerPage = data.OPTION_PAGE_SIZE || OPTION_PAGE_SIZE;
    function restrictFocusedIndex() {
        if (focusedIndex < 0) {
            focusedIndex = 0;
        }
        if (focusedIndex >= viewOptions.length) {
            focusedIndex = viewOptions.length - 1;
        }
    }
    function refreshOptions() {
        showOptions(
            viewOptions,
            filteredViewOptions,
            selectedIndices,
            focusedIndex,
            numPerPage,
            undefined,
            inputs.join(""),
        );
        whisper(
            `多选: [↑↓方向键]切换 [TAB]选择/取消 [CTRL+A]选择全部/取消全部 [ENTER]确认`,
            undefined,
            true,
        );
        if (inputs.length) {
            clearLine();
            debug(inputs.join(""), "", true);
        } else {
            whisper("可输入使用搜索", "", true);
        }
    }
    const selectedOptions = await new Promise((resolve, _reject) => {
        // listen for the "keypress" event
        /**
         *
         * @param {string} ch
         * @param {{ctrl: boolean, alt: boolean, shift: boolean, name: string }} key
         */
        const onKeyPress = (ch, key) => {
            let isKey = false;
            let inputsChg = false;
            if (key) {
                if (key.name === "return") {
                    isKey = true;
                    if (!selectedIndices.length && !confirmEmpty) {
                        clearLine();
                        confirmEmpty = true;
                        warn("\r当前未选择任何一项，回车再次确认", "");
                    } else {
                        process.stdin.off("keypress", onKeyPress);
                        process.stdin.pause();
                        toggleRawMode(false);
                        log("");
                        let ret;
                        switch (data.select_mode) {
                            case "key": {
                                ret = selectedIndices.map(
                                    (index) => optionKeys[index],
                                );
                                break;
                            }
                            case "index": {
                                ret = selectedIndices;
                                break;
                            }
                            case "all": {
                                ret = selectedIndices.map((focusedIndex) => {
                                    return {
                                        index: focusedIndex,
                                        key: optionKeys[focusedIndex],
                                        value: optionValues[focusedIndex],
                                    };
                                });
                                break;
                            }
                            default: {
                                ret = selectedIndices.map(
                                    (index) => optionValues[index],
                                );
                                break;
                            }
                        }
                        resetOptions();
                        resolve(ret);
                    }
                    return;
                } else if (key.name === "backspace" || key.name === "delete") {
                    isKey = true;
                    inputsChg = true;
                    inputs.pop();
                } else if (key.name === "left") {
                    isKey = true;
                    focusedIndex -= 1;
                    restrictFocusedIndex();
                } else if (key.name === "right") {
                    isKey = true;
                    focusedIndex += 1;
                    restrictFocusedIndex();
                } else if (key.name === "up") {
                    isKey = true;
                    const filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[Math.max(0, filterIdx - 1)],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "down") {
                    isKey = true;
                    const filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[
                            Math.min(
                                filterIdx + 1,
                                filteredViewOptions.length - 1,
                            )
                        ],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "pageup") {
                    isKey = true;
                    let filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    filterIdx = Math.max(0, filterIdx - numPerPage);
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[filterIdx],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "pagedown") {
                    isKey = true;
                    let filterIdx = filteredViewOptions.indexOf(
                        viewOptions[focusedIndex],
                    );
                    filterIdx = Math.min(
                        filteredViewOptions.length - 1,
                        filterIdx + numPerPage,
                    );
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[filterIdx],
                    );
                    restrictFocusedIndex();
                } else if (key.name === "home") {
                    isKey = true;
                    focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
                } else if (key.name === "end") {
                    isKey = true;
                    focusedIndex = viewOptions.indexOf(
                        filteredViewOptions[filteredViewOptions.length - 1],
                    );
                } else if (key.name === "tab") {
                    isKey = true;
                    if (focusedIndex !== -1) {
                        const idx = selectedIndices.indexOf(focusedIndex);
                        if (idx !== -1) {
                            selectedIndices.splice(idx, 1);
                        } else {
                            selectedIndices.push(focusedIndex);
                        }
                    }
                } else if (key.ctrl && key.name === "a") {
                    isKey = true;
                    // 当前是否已经全部选中
                    const all_selected = filteredViewOptions.every((v) => {
                        return selectedIndices.includes(viewOptions.indexOf(v));
                    });
                    const will_select = !all_selected;
                    for (let i = 0; i < filteredViewOptions.length; i++) {
                        const idx = viewOptions.indexOf(filteredViewOptions[i]);
                        const selected_idx = selectedIndices.indexOf(idx);
                        const selected = selected_idx !== -1;
                        if (will_select && !selected) {
                            selectedIndices.push(idx);
                        } else if (!will_select && selected) {
                            selectedIndices.splice(selected_idx, 1);
                        }
                    }
                } else if (key.ctrl && key.name === "c") {
                    throw "用户中止";
                    return;
                } else if (key.name === "escape") {
                    isKey = true;
                    inputsChg = true;
                    inputs.length = 0;
                } else if (key.ctrl) {
                    isKey = true;
                }
            }
            confirmEmpty = false;
            if (!isKey && ch) {
                inputs.push(ch);
                inputsChg = true;
            }
            if (inputsChg) {
                focusedIndex = 0;
                const inputStr = inputs.join("");
                const inputParts = inputStr?.split(/[, ]+/).filter((v) => v);
                filteredViewOptions = !inputParts
                    ? viewOptions
                    : viewOptions.filter((option, i) => {
                          const valueStr = JSON.stringify(option.value);
                          return inputParts.some((part) => {
                              return (
                                  part === i ||
                                  part === option.key ||
                                  valueStr.indexOf(part) !== -1
                              );
                          });
                      });
                if (inputStr && filteredViewOptions.length) {
                    focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
                }
            }
            refreshOptions();
        };
        process.stdin.on("keypress", onKeyPress);
        toggleRawMode(true);
        process.stdin.resume();
        refreshOptions();

        // 全自动返回所有选项
        if (shareData.FULL_AUTOMATIC || data.automatic) {
            onKeyPress(undefined, { ctrl: true, name: "a" });
            onKeyPress(undefined, { name: "return" });
        }
    });
    data.verbose && success(`已选择:${getPrint(selectedOptions, true)}`);
    return selectedOptions;
}

module.exports = {
    select,
    multiSelect,
};
